package data

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/min65535/gotechback/techback/biz"
	"gitee.com/min65535/gotechback/techback/util/json"
	"github.com/go-redis/redis/v8"
	"time"
)

type cacheRepo struct {
	data *Data
}

func (c *cacheRepo) SetCache(ctx context.Context, key string, val interface{}, expiration time.Duration) error {
	return c.data.Rdb.Set(ctx, key, val, expiration).Err()
}

func (c *cacheRepo) GetCache(ctx context.Context, key string) (string, error) {
	return c.data.Rdb.Get(ctx, key).Result()
}

func (c *cacheRepo) SAdd(ctx context.Context, key string, value interface{}) error {
	return c.data.Rdb.SAdd(ctx, key, json.StringifyJson(value)).Err()
}

func (c *cacheRepo) Del(ctx context.Context, key string) error {
	return c.data.Rdb.Del(ctx, key).Err()
}

func (c *cacheRepo) SetRateNumberInLimitTime(ctx context.Context, key string, numLimit int64, exp time.Duration) error {
	if key == "" || numLimit <= 0 || exp <= 0 {
		return errors.New("invalid rate param")
	}
	num, _ := c.getCnt(ctx, key)
	if num >= numLimit {
		return errors.New(fmt.Sprintf("rate limits %d times in %d Milliseconds", numLimit, int(exp.Milliseconds())))
	}
	_, err := c.incCnt(ctx, key, exp)
	return err
}

func (c *cacheRepo) getCnt(ctx context.Context, key string) (int64, error) {
	cnt, err := c.data.Rdb.Get(ctx, key).Int64()
	switch err {
	case redis.Nil:
		return 0, nil
	case nil:
		return cnt, nil
	default:
		return 0, nil
	}
}

func (c *cacheRepo) incCnt(ctx context.Context, key string, exp time.Duration) (int64, error) {
	cnt, err := c.data.Rdb.Incr(ctx, key).Result()
	if err != nil {
		return 0, err
	}
	if cnt == 1 {
		_, err = c.data.Rdb.Set(ctx, key, cnt, exp).Result()
	}
	return cnt, err
}

// NewCacheRepo .
func NewCacheRepo(data *Data) biz.Cache {
	return &cacheRepo{
		data: data,
	}
}
